﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PJ.Data;
using System.Collections;

namespace PJ.Business
{
    /// <summary>
    /// DataObjectService class
    /// </summary>
    /// <typeparam name="TIBusinessObject">Object to serve interface type</typeparam>
    /// <typeparam name="TIObjectData">Inner data object type</typeparam>
    /// <typeparam name="TIObjectDataProvider">Inner data object provider type</typeparam>
    public abstract class DataObjectService<TIBusinessObject, TIObjectData, TIObjectDataProvider> : IBusinessObjectService
        where TIBusinessObject : class, IBusinessObject
        where TIObjectData : class, IObjectData
        where TIObjectDataProvider : class, IObjectProvider<TIObjectData>
    {
        #region Constructors

        public DataObjectService(TIObjectDataProvider dataService)
        {
            if (dataService == null)
                throw new ArgumentNullException();
            m_DataService = dataService;
        }

        #endregion

        #region Public
        /// <summary>
        /// Gets the business object
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        /// <exception cref="DataNotFoundException">If the business object with the given global id does not exist.</exception>
        IBusinessObject IBusinessObjectService.GetData(string globalId)
        {
            return GetData(globalId);
        }

        /// <summary>
        /// Gets the business object using cache first, if not found in cache gets the business object from the provider.
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        /// <exception cref="DataNotFoundException">If the business object with the given global id does not exist.</exception>
        IBusinessObject IBusinessObjectService.CachedGet(string globalId)
        {
            return CachedGet(globalId);
        }

        /// <summary>
        /// Finds the business object.
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance if found else returns <see langref="null"/></returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        IBusinessObject IBusinessObjectService.Find(string globalId)
        {
            return Find(globalId);
        }

        /// <summary>
        /// Finds the business object using cache first, if not found in cache finds the business object from the provider.
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance if found else returns <see langref="null"/></returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        IBusinessObject IBusinessObjectService.CachedFind(string globalId)
        {
            return CachedFind(globalId);
        }

        /// <summary>
        /// Persists the business object.
        /// </summary>
        /// <param name="businessObject">Object to persist</param>
        /// <exception cref="ArgumentNullException">If <paramref name="businessObject"/> is <see langref="null"/>.</exception>
        void IBusinessObjectService.Persist(IBusinessObject businessObject)
        {
            Persist((TIBusinessObject)businessObject);
        }

        /// <summary>
        /// Persists the business object.
        /// </summary>
        /// <param name="businessObject">Object to persist</param>
        /// <exception cref="ArgumentNullException">If <paramref name="businessObject"/> is <see langref="null"/>.</exception>
        public void Persist(IEnumerable businessObject)
        {
            if (businessObject == null)
                throw new ArgumentNullException("businessObjectList");
            Persist((TIBusinessObject)businessObject);
        }

        /// <summary>
        /// Persists the business object and reloads it.
        /// </summary>
        /// <param name="businessObject">Object to persist</param>
        /// <returns>The persisted business object instance</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="businessObject"/> is <see langref="null"/>.</exception>
        IBusinessObject IBusinessObjectService.PersistAndGet(IBusinessObject businessObject)
        {
            return PersistAndGet((TIBusinessObject)businessObject);
        }


        /// <summary>
        /// Checks if the business object exists.
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns><see langref="true"/> if found else <see langref="false"/></returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        public bool Exist(string globalId)
        {
            return DataService.Exist(globalId);
        }

        /// <summary>
        /// Removes the business object from the business source
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        /// <exception cref="DataNotFoundException">If the business object with the given global id does not exist.</exception>
        public void Delete(string globalId)
        {
            DataService.Delete(globalId);
        }

        /// <summary>
        /// Creates a new instance of the concrete class
        /// </summary>
        /// <returns>The business object instance</returns>
        IBusinessObject IBusinessObjectService.CreateNewItem()
        {
            return CreateNewItem();
        }


        /// <summary>
        /// Gets the business object
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        /// <exception cref="DataNotFoundException">If the business object with the given global id does not exist.</exception>
        public TIBusinessObject GetData(string globalId)
        {
            return CreateNewItem(DataService.GetData(globalId));
        }

        /// <summary>
        /// Gets the business object using cache first, if not found in cache gets the business object from the provider.
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        /// <exception cref="DataNotFoundException">If the business object with the given global id does not exist.</exception>
        public TIBusinessObject CachedGet(string globalId)
        {
            return CreateNewItem(DataService.CachedGet(globalId));
        }

        /// <summary>
        /// Finds the business object.
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance if found else returns <see langref="null"/></returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        public TIBusinessObject Find(string globalId)
        {
            return CreateNewItem(DataService.Find(globalId));
        }

        /// <summary>
        /// Finds the business object using cache first, if not found in cache finds the business object from the provider.
        /// </summary>
        /// <param name="globalId">Object global id</param>
        /// <returns>The business object instance if found else returns <see langref="null"/></returns>
        /// <exception cref="ArgumentNullException">If <paramref name="globalId"/> is <see langref="null"/> or empty.</exception>
        public TIBusinessObject CachedFind(string globalId)
        {
            return CreateNewItem(DataService.CachedFind(globalId));
        }

        /// <summary>
        /// Persists the business object.
        /// </summary>
        /// <param name="businessObject">Object to persist</param>
        /// <returns>The persisted business object instance</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="businessObject"/> is <see langref="null"/>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="businessObject"/> global id is <see langref="null"/>.</exception>
        public void Persist(TIBusinessObject businessObject)
        {
            if (businessObject == null)
                throw new ArgumentNullException("businessObject");
            if (string.IsNullOrEmpty(businessObject.GlobalId))
                throw new ArgumentException("businessObject.GlobalId");
            DataService.Persist(GetDataObject(businessObject));
        }

        /// <summary>
        /// Persists the business object and reloads it.
        /// </summary>
        /// <param name="businessObject">Object to persist</param>
        /// <returns>The persisted business object instance</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="businessObject"/> is <see langref="null"/>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="businessObject"/> global id is <see langref="null"/>.</exception>
        public TIBusinessObject PersistAndGet(TIBusinessObject businessObject)
        {
            if (businessObject == null)
                throw new ArgumentNullException("businessObject");
            if (string.IsNullOrEmpty(businessObject.GlobalId))
                throw new ArgumentException("businessObject.GlobalId");
            businessObject.InitData(DataService.PersistAndGet(GetDataObject(businessObject)));
            return businessObject;
        }

        /// <summary>
        /// Creates a new instance of the concrete class
        /// </summary>
        /// <returns>The business object instance</returns>
        public abstract TIBusinessObject CreateNewItem();

        #endregion

        #region Protected

        protected TIObjectDataProvider DataService
        {
            get { return m_DataService; }
            set { m_DataService = value; }
        }

        protected IObjectData GetDataObject(TIBusinessObject bo)
        {
            IObjectDataContainer temp = bo as IObjectDataContainer;
            if (temp == null)
            {
                TIBusinessObject newData = CreateNewItem();
                newData.CopyFrom(bo);
                return ((IObjectDataContainer)newData).ObjectData;

            }
            return temp.ObjectData;
        }

        /// <summary>
        /// Creates a new instance of the concrete class
        /// </summary>
        /// <returns>The business object instance</returns>
        protected TIBusinessObject CreateNewItem(TIObjectData data)
        {
            TIBusinessObject bo = CreateNewItem();
            bo.InitData(data);
            return bo;
        }
        /*

        protected COL DataToObjectCollection<TD, TI, COL>(IEnumerable<TD> dataList)
            where TI : class, IBusinessObject
            where TD : class, IObjectData
            where COL : ICollection<TI>, new()
        {
            if (dataList == null)
                throw new ArgumentNullException();
            COL list = new COL();
            foreach (TD data in dataList)
                list.Add((TI)(object)CreateNewItem((TIObjectData)(object)data));
            return list;
        }
        protected COL ObjectToDataCollection<TI, TD, COL>(IEnumerable<TI> objectList)
            where TI : class, TIBusinessObject
            where TD : class, TIObjectData
            where COL : ICollection<TD>, new()
        {
            if (objectList == null)
                throw new ArgumentNullException();
            COL list = new COL();
            foreach (TI obj in objectList)
            {
                list.Add(GetDataObject(obj));
            }
            return list;
        }
        
        protected List<TD> ObjectToDataCollection<TI, TD>(IEnumerable<TI> objectList)
            where TI : IBusinessObject
            where TD : IObjectData
        {
            return ObjectToDataCollection<TI, TD, List<TD>>(objectList);
        }
         */
        #endregion

        #region Private
        #endregion

        #region Fields

        private TIObjectDataProvider m_DataService;

        #endregion
    }
}
